package com.motu.monstercity.server.game.timedtask;

import com.motu.monstercity.module.common.servermanager.ServerInfo;
import com.motu.monstercity.module.common.servermanager.ServerManager;
import com.motu.monstercity.server.game.base.AllParam;
import com.motu.monstercity.server.game.commondata.act.Act;
import com.motu.monstercity.server.game.commondata.feast.FeastConstant;
import com.motu.monstercity.server.game.commondata.feast.FeastMain;
import com.motu.monstercity.server.game.logic.FeastManager;
import com.motu.monstercity.server.game.logic.FightManager;
import com.motu.monstercity.server.game.logic.WorldManager;
import com.motu.monstercity.server.game.userdata.ServerFeast;
import com.motu.monstercity.server.game.userdata.ServerSnatchOffer;
import com.motu.monstercity.server.game.userdata.UserWorldTeam;
import com.motu.vertx.module.utility.base.ServerConfig;
import com.motu.vertx.module.utility.main.MainService;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.*;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 延迟队列管理类
 */
public class DelayQueueManager {

	public final static Logger logger = LoggerFactory.getLogger(DelayQueueManager.class);

	/**
	 * 注意：这里定义的定时任务队列不能在WorkVerticle中操作，因为队列只存在主进程，而WorkVerticle可能有多个进程
	 */

	// 定时任务配置（key为任务ID）
	private static ConcurrentHashMap<Integer, TimeTaskConfig> timeTaskConfigMap = new ConcurrentHashMap<>();

	// 固定时间点定时任务
	private static ExecutorService execTimeTask = Executors.newSingleThreadExecutor();// 定时任务队列执行者
	private static DelayQueue<GameTimeTask> timeTaskQueue = new DelayQueue<>(); // 定时任务执行队列

	// 活动结算任务
	private static ExecutorService execActClose = Executors.newSingleThreadExecutor();// 活动结算任务队列执行者
	private static DelayQueue<ActCloseTask> actCloseQueue = new DelayQueue<>(); // 活动结算任务队列

	// 活动结束任务
	private static ExecutorService execActEnd = Executors.newSingleThreadExecutor();// 活动结束任务队列执行者
	private static DelayQueue<ActEndTask> actEndQueue = new DelayQueue<>(); // 活动结束任务队列

	// 私人派对结算定时任务
	private static ExecutorService serverfeastService = Executors.newSingleThreadExecutor();
	private static DelayQueue<ServerfeastTask> serverfeastQueue = new DelayQueue<>();

	// 世界Boss结算定时任务
	private static ExecutorService worldBossService = Executors.newSingleThreadExecutor();
	private static DelayQueue<WorldBossTask> worldBossQueue = new DelayQueue<>();

	// 物资争夺的悬赏的定时任务
	private static ExecutorService serverSnatchOfferService = Executors.newSingleThreadExecutor();
	private static DelayQueue<ServerSnatchOfferTask> serverSnatchOfferQueue = new DelayQueue<>();

	public static void init() {
		Vertx vertx = MainService.getVertx();
		//这里要延迟初始化，不然可能初始化执行任务时，集群还没有加入成功
		vertx.setTimer(30000l, handler -> {
			logger.info("DelayQueueManager.init !!!!!!");
			initExecutorService();// 初始化定时任务执行器
			initTimeTaskConfig();// 初始化定时任务配置
			initAct();// 初始化活动定时任务
			initServerfeastTask();// 初始化私人派对结算定时任务
			initServerSnatchOfferTask();// 初始话物资争夺悬赏的定时任务
			initWorldBossTask();// 初始化世界boss战斗的定时任务
		});
	}

	/**
	 * 初始化执行器
	 */
	public static void initExecutorService() {
		// 执行定时任务线程
		execTimeTask.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					timeTaskQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});
		// 执行活动结算任务线程
		execActClose.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					actCloseQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});
		// 执行活动结束任务线程
		execActEnd.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					actEndQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});
		// 执行私人派对结算任务线程
		serverfeastService.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					serverfeastQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});

		// 执行世界Boss任务线程
		worldBossService.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					worldBossQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});

		// 执行物资争夺悬赏结算任务线程
		serverSnatchOfferService.execute(() -> {
			try {
				while (!Thread.interrupted()) {
					serverSnatchOfferQueue.take().run();
				}
			} catch (InterruptedException e) {
				logger.error(Tool.getException(e));
			}
		});
	}

	/***
	 * 初始化定时任务配置
	 */
	public static void initTimeTaskConfig() {
		if (!ServerConfig.isTimedTaskServer()) {
			// 限制只能在定时任务服运行，避免重载配置导致误操作
			logger.warn("!!!!initTimeTaskConfig cannot be run outside of timed_task server!!!!");
			return;
		}
		logger.info("initTimeTaskConfig start");
		// 查询游戏内当前执行的定时任务
		ConcurrentHashMap<Integer, Boolean> map = new ConcurrentHashMap<>();
		String sql = "SELECT * FROM time_task;";//读取游戏服的定时任务记录表，看有哪些任务已经存在了
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getGamePool(), sql, new JsonArray(), res -> {
			if (res.succeeded()) {
				JsonArray aryGametimetask = res.result();
				if (aryGametimetask != null) {
					for (int i = 0, size = aryGametimetask.size(); i < size; i++) {
						JsonObject obj = aryGametimetask.getJsonObject(i);
						int taskId = obj.getInteger("task_id");
						map.put(taskId, true);
						logger.info("init timeTask id:" + taskId);
					}
				}
				String sql2 = "select * from time_task_config;";
				MysqlTool.queryJsonArrayFromDb(MysqlClient.getCommonPool(), sql2, new JsonArray(), res2 -> {
					if (res2.succeeded()) {
						JsonArray ary = res2.result();
						ConcurrentHashMap<Integer, TimeTaskConfig> temp = new ConcurrentHashMap<>();
						for (int i = 0, size = ary.size(); i < size; i++) {
							TimeTaskConfig timeTaskConfig = new TimeTaskConfig(ary.getJsonObject(i));
							temp.put(timeTaskConfig.getId(), timeTaskConfig);
							logger.info("init TimeTaskConfig id:" + timeTaskConfig.getId());
							// 如果当前执行的定时任务没有配置的信息，则添加一条
							if (!map.containsKey(timeTaskConfig.getId())) {
								// 这里要同步写入，否则后续初始化定时任务的时候可能读取不到最新的数据
								Promise<Object> promise = Promise.promise();
								addTimetask(timeTaskConfig.getId(), timeTaskConfig.getNextTime(), promise);
								Future.await(promise.future());
							}
						}
						timeTaskConfigMap.clear();
						timeTaskConfigMap = temp;
						initTimeTask();// 初始化配置之后才执行初始化任务
					} else {
						logger.error(res2.cause().getMessage());
					}
				});
			} else {
				logger.error(res.cause().getMessage());
			}
		});

	}

	/***
	 * 获取定时任务配置
	 * @param id 任务ID
	 */
	public static TimeTaskConfig getTimetaskconfig(int id) {
		TimeTaskConfig result = timeTaskConfigMap.get(id);
		if (result == null) {
			logger.error("getTimetaskconfig error id = " + id);
		}
		return result;
	}

	/***
	 * 初始化游戏定时任务（要在游戏初始化最后面执行，不然执行的任务可能读取不到其他初始化信息）
	 */
	public static void initTimeTask() {
		logger.info("initTimeTask start");
		String sql = "select * from time_task;";//读取游戏服的定时任务记录表
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getGamePool(), sql, new JsonArray(), res -> {
			if (res.succeeded()) {
				JsonArray ary = res.result();
				logger.info("initTimeTask list :" + ary.size());
				if (!timeTaskQueue.isEmpty()) {
					// 避免重载initTimeTaskConfig，往队列里重复添加定时任务
					timeTaskQueue.clear();
					logger.info("initTimeTask timeTaskQueue clear !!!");
				}
				for (int i = 0, size = ary.size(); i < size; i++) {
					JsonObject obj = ary.getJsonObject(i);
					int taskId = obj.getInteger("task_id");
					long time = obj.getLong("task_time");// 执行时间
					TimeTaskConfig config = getTimetaskconfig(taskId);
					if (config != null) {
						if (time <= TimeUtils.getCurTime()) {
							timeTaskQueue.put(new GameTimeTask(taskId, time, config.getMethod()));
						} else {
							long nextTime = config.getNextTime();
							timeTaskQueue.put(new GameTimeTask(config.getId(), nextTime, config.getMethod()));// 按配置的时间为准,有可能会调整执行时间
							if (nextTime != time) {
								updateTimetaskNextTime(config.getId(), nextTime, updateRes -> {
								});
							}
						}
					}
				}
			}
		});
	}

	/**
	 * 重载参数后，更新定时任务
	 */
	public static void updateTimeTaskByParamsReload() {
		// 启动时initParams在initTimeTaskConfig之前执行
		if (!ServerConfig.isTimedTaskServer()) {
			return; // 只有TimeTask服才有定时任务队列
		}
		for (TimeTaskConfig config : timeTaskConfigMap.values()) {
			for (GameTimeTask task : timeTaskQueue) {
				if (config.getId() != task.getTaskId()) {
					continue;
				}
				long nextTime = config.getNextTime();
				if (nextTime == task.getTime()) {
					continue;
				}
				logger.info("updateTimeTaskByParamsReload task id = " + config.getId()
						+ " old time = " + task.getTime() + " new time = " + nextTime);
				timeTaskQueue.remove(task);
				updateTimetaskNextTime(config.getId(), nextTime, updateRes -> {
				});
				timeTaskQueue.put(new GameTimeTask(config.getId(), nextTime, config.getMethod()));
				break;
			}
		}
	}

	/***
	 * 更新定时任务下次执行时间
	 */
	public static void updateTimetaskNextTime(int taskId, long time, Handler<AsyncResult<Object>> handler) {
		String sql = "UPDATE time_task SET task_time = ? WHERE task_id = ?;";
		JsonArray params = new JsonArray();
		params.add(time);
		params.add(taskId);
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler);
	}

	/***
	 * 添加游戏定时任务
	 * @param taskid 任务ID
	 * @param time 任务执行时间
	 */
	public static void addTimetask(int taskid, long time, Handler<AsyncResult<Object>> handler) {
		String sql = "INSERT INTO time_task(task_id, task_time) VALUES(?,?)";
		JsonArray params = new JsonArray();
		params.add(taskid);
		params.add(time);
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler);
	}

	public static void addTimeTaskQueue(GameTimeTask task) {
		timeTaskQueue.put(task);
	}

	/**
	 * 初始化活动信息
	 */
	public static void initAct() {
		logger.info("initAct start");
		// 清空定时任务
		actCloseQueue.clear();
		actEndQueue.clear();
		// 添加定时任务
		String sql = "SELECT * FROM act where status = 0 and end_time > UNIX_TIMESTAMP() order by end_time;";
		JsonArray params = new JsonArray();
		long curTime = TimeUtils.getCurTime();
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, params, handler -> {
			if (handler.succeeded()) {
				JsonArray array = handler.result();
				if (array != null && array.size() > 0) {
					for (int i = 0, size = array.size(); i < size; i++) {
						Act act = new Act(array.getJsonObject(i));
						if (act.getCloseTime() > curTime) {
							logger.info("close actId:" + act.getId() + " close time:" + act.getCloseTime());
							setActCloseTask(act);// 设置活动结算定时任务
						}
						setActEndTask(act);// 设置活动结束定时任务
					}
				}
			} else {
				logger.error(handler.cause().getMessage());
			}
		});
	}

	// 设置活动结算定时任务
	public static void setActCloseTask(Act act) {
		actCloseQueue.put(new ActCloseTask(act.getId(), act.getType(), act.getCloseTime(), act.getParam()));
	}

	// 设置活动结束定时任务
	public static void setActEndTask(Act act) {
		actEndQueue.put(new ActEndTask(act.getId(), act.getType(), act.getEndTime(), act.getParam()));
	}

	/**
	 * 初始化私人派对结算定时任务
	 */
	public static void initServerfeastTask() {
		logger.info("initServerfeastTask start");
		// 清空定时任务
		serverfeastQueue.clear();
		// 添加定时任务
		ConcurrentHashMap<Integer, ServerInfo> serverList = ServerManager.getServerList();// 从内存里读取服务器列表
		for (ServerInfo serverInfo : serverList.values()) {
			int serverId = serverInfo.getServerId();
			List<ServerFeast> list = FeastManager.getAllServerFeastList(serverId);// 获取所有派对，包括已结束的
			for (ServerFeast serverFeast : list) {
				FeastMain feastMain = FeastManager.getFeastMain(serverFeast.getFeastMainId());
				JsonObject task = new JsonObject();
				task.put("uniq_key", serverFeast.getUniqKey());
				task.put("server_id", serverId);
				task.put("end_time", serverFeast.getAddTime() + feastMain.getDurationTime());
				logger.info("initServerfeastTask " + task.toString());
				serverfeastQueue.put(new ServerfeastTask(task));
			}
		}
	}

	public static void addServerfeastQueue(ServerfeastTask task) {
		serverfeastQueue.put(task);
	}

	/**
	 * 初始化世界Boss战斗定时任务
	 */
	public static void initWorldBossTask() {
		logger.info("initWorldBossTask start");
		// 清空定时任务
		worldBossQueue.clear();
		// 添加定时任务
		// 出发的部队，且是boss事件的
		String sql = "SELECT * FROM user_world_team where status = 1 and location_id = 4001  order by start_time;";
		JsonArray params = new JsonArray();
		long curTime = TimeUtils.getCurTime();
		MysqlTool.queryJsonArrayFromDb(MysqlClient.getGamePool(), sql, params, handler -> {
			if (handler.succeeded()) {
				JsonArray array = handler.result();
				if (array != null && !array.isEmpty()) {
					for (int i = 0, size = array.size(); i < size; i++) {
						JsonObject jsonObject = array.getJsonObject(i);
						long userId = jsonObject.containsKey("user_id") ? jsonObject.getLong("user_id") : 0;
						int teamId = jsonObject.containsKey("team_id") ? jsonObject.getInteger("team_id") : 0;
						int timeLeft = jsonObject.containsKey("time_left") ? jsonObject.getInteger("time_left") : 0;
						long updateTime = jsonObject.containsKey("update_time") ? jsonObject.getLong("update_time") : 0;
						String uniqeId = jsonObject.containsKey("uniqe_id") ? jsonObject.getString("uniqe_id") : "";
						int reduceTime = jsonObject.containsKey("reduce_time") ? jsonObject.getInteger("reduce_time") : 0;
						JsonObject task = new JsonObject();
						task.put("user_id", userId);
						task.put("team_id", teamId);
						task.put("unique_id", uniqeId);
						task.put("end_time", updateTime + timeLeft - reduceTime);
                        logger.info("initWorldBossTask {}", task.toString());
						worldBossQueue.put(new WorldBossTask(task));
					}
				}
			} else {
				logger.error(handler.cause().getMessage());
			}
		});
	}


	public static void addWorldBossQueue(WorldBossTask task) {
		worldBossQueue.put(task);
	}


	/**
	 * 初始化物资争夺悬赏的结算定时任务
	 */
	public static void initServerSnatchOfferTask() {
		logger.info("initServerSnatchOfferTask start");
		// 清空定时任务
		serverSnatchOfferQueue.clear();
		// 添加定时任务
		ConcurrentHashMap<Integer, ServerInfo> serverList = ServerManager.getServerList();// 从内存里读取服务器列表
		for (ServerInfo serverInfo : serverList.values()) {
			int serverId = serverInfo.getServerId();
			List<ServerSnatchOffer> list = FightManager.getServerSnatchOfferList(serverId);// 获取所有的悬赏，包括已结束的
			for (ServerSnatchOffer serverSnatchOffer : list) {
				if (serverSnatchOffer.getIsSendReward() == 0) {
					JsonObject task = new JsonObject();
					task.put("user_id", serverSnatchOffer.getUserId());
					task.put("server_id", serverId);
					task.put("end_time", serverSnatchOffer.getPublishTime() + AllParam.SNATCH_OFFER_DURATION_TIME);
					logger.info("initServerSnatchOfferTask " + task.toString());
					serverSnatchOfferQueue.put(new ServerSnatchOfferTask(task));
				}
			}
		}
	}

	public static void addServerSnatchOfferQueue(ServerSnatchOfferTask task) {
		serverSnatchOfferQueue.put(task);
	}
}
